package TXPathFinder;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Timer;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Shape3D;
import javax.vecmath.Color3f;

/**
*       \class	DataModel
*	\brief	Tout les données importantes sont stockées ici
**/
public class DataModel
{

    private Viewer viewer;
    public Hashtable listAvatar;
    private Color3f currentColor;
    private float rayonAv = 0.5f;
    private Timer time;
    private MoveTimerTask task;
    private boolean isTimer;
    private PathFindingWrapper pf;
    private Hashtable<String, Shape3D> shapeDecor;
    //false si rien n'est selectionné
    private boolean focus3D;
    //true si un avatar est selectionné
    private boolean isAvatarSelected;
    private CurrentPath currentPath;
    //la position du higlight bloc
    private Coordonnee posHighLight;
    //mode debug activé ou non
    private boolean modeDebug;
    //stockage des ghosts
    public ArrayList<BranchGroup> listGhosts;
    private int avatarCurrent;
    /** L'instance statique */
    private static DataModel instance;

    /**
    *	\brief	Constructeur
    **/
    private DataModel() {
        currentColor = new Color3f(1.0f,1.0f,1.0f);
        listAvatar = new Hashtable<Integer, Avatar>();
        listGhosts = new ArrayList<BranchGroup>();
        shapeDecor = new Hashtable<String, Shape3D>();
        posHighLight = new Coordonnee(0, 0, 0);
        isTimer = false;
        focus3D = false;
        isAvatarSelected = false;
        currentPath = new CurrentPath();
        pf = new PathFindingWrapper();
        avatarCurrent = -1;
        modeDebug = false;
    }

    /**
    *	\brief	Accesseur
    *   \return Le data modèle (pattern Singleton)
    **/
    public static DataModel getInstance() {
        if (null == instance) { // Premier appel
            instance = new DataModel();
        }
        return instance;
    }
    
    /**
    *	\brief	Accesseur
    *   \return L'avatar courant (selectionné)
    **/
    public int getAvatarCurrent() {
        return avatarCurrent;
    }

    /**
    *	\brief	Accesseur
    *   \param avatarCurrent L'avatar courant (selectionné)
    **/
    public void setAvatarCurrent(int avatarCurrent) {
        this.avatarCurrent = avatarCurrent;
    }

    /**
    *	\brief	Accesseur
    *   \return True si un avatar est selectionné
    **/
    public boolean isIsAvatarSelected() {
        return isAvatarSelected;
    }

    /**
    *	\brief	Accesseur
    *   \param x Coordonnée x
     *  \param y Coordonnée y
    **/
    public void setCoordHighlight(int x, int y)
    {
        posHighLight.setCoordX(x);
        posHighLight.setCoordY(y);
    }

    /**
    *	\brief	Accesseur
    *   \param isAvatarSelected True pour préciser qu'un avatar es tselectionné
    **/
    public void setIsAvatarSelected(boolean isAvatarSelected)
    {
        this.isAvatarSelected = isAvatarSelected;
    }

    /**
    *	\brief	Accesseur
    *   \return La map cognitive en cour (de l'avatar courant)
    **/
    public CurrentPath getCurrentPath() {
        return currentPath;
    }

    /**
    *	\brief	Associe une forme 3D à son identifiant dans une HashTable
    *   \param key L'identifiant
    *   \param shape La forme 3D
    **/
    public void addShapeTable(String key, Shape3D shape)
    {
        shapeDecor.put(key, shape);
    }

    /**
    *	\brief	recupère une forme à partir de son identifiant
    *   \param key L'identifiant de la forme
    *   \return La forme (Shape3D)
    **/
    public Shape3D getFromShapeTable(String key)
    {
        return shapeDecor.get(key);
    }

    /**
    *	\brief	Accesseur
    *   \return Le viewer3D
    **/
    public Viewer getViewer() {
        return viewer;
    }

    /**
    *	\brief	Accesseur
    *   \return Le handler sur le programme C++
    **/
    public PathFindingWrapper getPf()
    {
            return pf;
    }

    /**
    *	\brief	Lance le deplacement des avatars
    **/
    public void startMove()
    {
        if( isTimer == false )
        {
            time = new Timer();
            task = new MoveTimerTask();
            time.schedule(task,0,130);
            isTimer = true;
        }
    }

    /**
    *	\brief	Arrete le deplacement des avatars
    **/
    public void endMove()
    {
        time.cancel();
        isTimer = false;
    }

    /**
    *	\brief	Ajoute un ghost à la carte 3D à la position spécifiée
     *  \param x La position x du ghost
     *  \param y La position y du ghost
    **/
    public void addGhost(int x, int y)
    {
        if( modeDebug )
        {
            BranchGroup groupeTmp;
            groupeTmp = ShapeService.createGhost(x, y);
            groupeTmp.compile();
            listGhosts.add(groupeTmp);
            viewer.addBranchToUniverse(groupeTmp);
        }
    }

    /**
    *	\brief	Détruit tout les ghosts presents dans la map 3D
    **/
    public void deleteAllGhost()
    {
        int size = listGhosts.size();
        for ( int i = 0 ; i < size ; i++ )
            viewer.removeBranchToUniverse(listGhosts.get(i));
    }

    /**
    *	\brief	Ajoute un avatar aléatoirement dans la map 3D
    **/
    public void addAvatar()
    {
        addAvatar(DataModel.getInstance().getPf().addAgent());
    }

    /**
    *	\brief	Ajoute un avatar dans la map 3D, dont la position à été définit par le user, en selectionnant un bloc
    **/
    public void addAvatarWithHighlight()
    {
        //si le focus et sur un bloc alors on recupere les coordonnées et on ajout un avatar à l'emplacement
        if( focus3D /*&& !isAvatarSelected*/ )
        {
            addAvatar(DataModel.getInstance().getPf().addAgent(posHighLight.getCoordY(),posHighLight.getCoordX()));
        }
    }

    /**
    *	\brief	Ajoute un avatar
    **/
    public void addAvatar(Coordonnee coo)
    {
        listAvatar.put(coo.getiD(),new Avatar(currentColor, 0.5f, coo.getCoordX()+rayonAv, ShapeService.getLargeur()-coo.getCoordY()-rayonAv,coo.getiD()));
        BranchGroup groupeTmp;
        groupeTmp = ((Avatar)listAvatar.get(coo.getiD())).getBG();
        groupeTmp.compile();
        viewer.addBranchToUniverse(groupeTmp);
    }

    void removeAvatar(int id)
    {
        viewer.removeBranchToUniverse(((Avatar)listAvatar.get(id)).getBG());
        listAvatar.remove(id);
    }

    /**
    *	\brief	Récupère la liste des avatars
    *   \return La liste des avatars
    **/
    public Hashtable<Integer, Avatar> getListAvatar()
    {
        return listAvatar;
    }

    /**
    *	\brief	Récupère un avatar en fonction de son idex
     *  \param index Index de l'avatar
    *   \return L'avatar spécifié par son index
    **/
    public Avatar getAvatar(int key)
    {
        return (Avatar)listAvatar.get(key);
    }

    /**
    *	\brief	Accesseur
    *   \param view Le viewer à stocker
    **/
    public void setViewer(Viewer view)
    {
        viewer = view;
    }

    /**
    *	\brief	Accesseur
    *   \param color La couleur que prendra les prochains avatars crées
    **/
    public void setCurrentColor(Color3f color)
    {
        currentColor = color;
    }

    /**
    *	\brief	Accesseur
    *   \return True si on est en mode debug, false sinon
    **/
    public boolean isModeDebug() {
        return modeDebug;
    }

    /**
    *	\brief	Accesseur
    *   \param modeDebug True si on est en mode debug, false sinon
    **/
    public void setModeDebug(boolean modeDebug) {
        this.modeDebug = modeDebug;
    }

    /**
    *	\brief	Accesseur
    *   \param modeDebug True si un bloc ou un avatar est selectionné sur la map, false sinon
    **/
    public void setFocusTaken(boolean bool) {
        focus3D = bool;
    }

    /**
    *	\brief	Accesseur
    *   \return True si un bloc ou un avatar est selectionné sur la map, false sinon
    **/
    public boolean isFocusTaken() {
        return focus3D;
    }

    /**
    *	\brief	Envoie au programme C++ la destination choisit pour l'avatar courant
    *   \param x La position x
    *   \param y La position y
    **/
    void setGotoAgent(int x, int y) {
        pf.goTo(avatarCurrent, y, x);
    }


}
